// cookie 管理工具类-单例模式
package config

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"github.com/gin-gonic/gin"
	"sync"
	"time"
)

// 单例模式   CookieInstance()单例实例的公共方法
var (
	instance *CookieManager // instance 和 once 作为包级私有变量，外部无法直接访问
	once     sync.Once      //确保 CookieManager 实例只被创建一次，且线程安全
)

func CookieInstance() *CookieManager {
	once.Do(func() {
		instance = &CookieManager{
			coo: make(map[string]string),
		}
	})
	return instance
}

// 定义结构体
type CookieManager struct {
	// map 存放cookie
	coo map[string]string
	mu  sync.RWMutex
}

// new 一个示例
//func NewCookieManager() *CookieManager {
//	return &CookieManager{
//		coo: make(map[string]string),
//	}
//}

// 生成cookie 并设置cookie
func (cm *CookieManager) Generate(key string, max int, c *gin.Context) string {

	// 随机生成cookie
	val := cm.generateCookie(16)
	cm.mu.Lock()
	defer cm.mu.Unlock()

	cm.coo[key] = val //存储值

	// 设置 cookie
	// 将生成的val 作为cookie：key
	c.SetCookie(val, key, max, "/", "", false, true)

	return val
}

// 校验用户名是否在coo容器
func (cm *CookieManager) CheckCooKey(key string) (string, bool) {
	var chekFlag bool = false
	for k, v := range cm.coo {
		if k == key {
			chekFlag = true
			return v, chekFlag
		}
	}

	return "", chekFlag
}

// 校验cookie 是否存在
func (cm *CookieManager) CheckExist(val string, c *gin.Context) (string, bool) {

	_, err := c.Cookie(val)
	if err != nil {
		return "", false
	}

	cm.mu.RLock()
	defer cm.mu.RUnlock()

	var chekFlag bool = false
	for k, v := range cm.coo {
		if v == val {
			chekFlag = true
			return k, true
		}
	}

	return "", chekFlag
}

// 获取cookie的值
func (cm *CookieManager) GetCookie(key string, c *gin.Context) string {
	_, err := c.Cookie(key)
	if err != nil {
		return ""
	}

	cm.mu.RLock()
	defer cm.mu.RUnlock()

	val, _ := cm.coo[key]
	return val
}

// 删除cookie
func (cm *CookieManager) DelCookie(key string, c *gin.Context) bool {
	_, err := c.Cookie(key)
	if err != nil {
		return false
	}

	cm.mu.Lock()
	defer cm.mu.Unlock()

	// 从容器中删除
	delete(cm.coo, key)

	// 设置浏览器cookie过期
	c.SetCookie(key, "", -1, "/", "", false, true)
	return true
}

// 获取当前cookie数量
func (cm *CookieManager) Count() int {
	cm.mu.Lock()
	defer cm.mu.Unlock()

	return len(cm.coo)
}

// 内部方法：生成随机 token
func (cm *CookieManager) generateCookie(length int) string {

	bytes := make([]byte, length)
	_, err := rand.Read(bytes)
	if err != nil {
		// 如果随机数生成失败，使用时间戳作为备选方案
		return fmt.Sprintf("%d", time.Now().UnixNano())
	}
	return hex.EncodeToString(bytes)
}
